1 module scaler;
2 
3 import std.algorithm;
4 import std.math;
5 import std.stdio;
6 
7 // Retro scalers for NES native 256 * 240 RGBA image
8 
9 private enum NATIVE_WIDTH = 256;
10 private enum NATIVE_HEIGHT = 240;
11 
12 interface Scaler {
13     int factor();
14     ubyte* scale(ubyte* src);
15 }
16 
17 class NoScaler : Scaler {
18     int factor() {
19         return 1;
20     }
21 
22     ubyte* scale(ubyte* src) {
23         return src;
24     }
25 }
26 
27 class Scale2x : Scaler {
28     this() {
29         this.buffer = new ubyte[NATIVE_WIDTH * 2 * NATIVE_HEIGHT * 2 * 4];
30     }
31 
32     int factor() {
33         return 2;
34     }
35 
36     ubyte* scale(ubyte* src) {
37         ubyte* dst = this.buffer.ptr;
38 
39         int width = NATIVE_WIDTH;
40         int height = NATIVE_HEIGHT;
41         int srcPitch = width * 4;
42         int dstPitch = width * 2 * 4;
43 
44         int looph, loopw;
45 
46         uint e0, e1, e2, e3, b, d, e, f, h;
47 
48         for (looph = 0; looph < height; ++looph) {
49             for(loopw = 0; loopw < width; ++loopw) {
50                 b = *cast(uint*)(src + (max(0, looph - 1) * srcPitch) + (4 * loopw));
51                 d = *cast(uint*)(src + (looph * srcPitch) + (4 * max(0, loopw - 1)));
52                 e = *cast(uint*)(src + (looph * srcPitch) + (4 * loopw));
53                 f = *cast(uint*)(src + (looph * srcPitch) + (4 * min(width - 1, loopw + 1)));
54                 h = *cast(uint*)(src + (min(height - 1, looph + 1) * srcPitch) + (4 * loopw));
55             
56                 e0 = d == b && b != f && d != h ? d : e;
57                 e1 = b == f && b != d && f != h ? f : e;
58                 e2 = d == h && d != b && h != f ? d : e;
59                 e3 = h == f && d != h && b != f ? f : e;
60 
61                 *cast(uint*)(dst + looph * 2 * dstPitch + loopw * 2 * 4) = e0;
62                 *cast(uint*)(dst + looph * 2 * dstPitch + (loopw * 2 + 1) * 4) = e1;
63                 *cast(uint*)(dst + (looph * 2 +1)* dstPitch + loopw * 2 * 4) = e2;
64                 *cast(uint*)(dst + (looph * 2 +1) * dstPitch + (loopw * 2 + 1) * 4) = e3;
65             }
66         }
67 
68         return dst;
69     }
70 
71     private:
72         ubyte[] buffer;
73 }
74 
75 class Scale3x : Scaler {
76     this() {
77         this.buffer = new ubyte[NATIVE_WIDTH * 3 * NATIVE_HEIGHT * 3 * 4];
78     }
79 
80     int factor() {
81         return 3;
82     }
83 
84     ubyte* scale(ubyte* src) {
85         ubyte* dst = this.buffer.ptr;
86 
87         int width = NATIVE_WIDTH;
88         int height = NATIVE_HEIGHT;
89         int srcPitch = width * 4;
90         int dstPitch = width * 3 * 4;
91 
92         int looph, loopw;
93 
94         uint e0, e1, e2, e3, e4, e5, e6, e7, e8;
95         uint a, b, c, d, e, f, g, h, i;
96 
97         for (looph = 0; looph < height; ++looph) {
98             for(loopw = 0; loopw < width; ++loopw) {
99                 a = *cast(uint*)(src + (max(0, looph - 1) * srcPitch) + (4 * max(0, loopw - 1)));
100                 b = *cast(uint*)(src + (max(0, looph - 1) * srcPitch) + (4 * loopw));
101                 c = *cast(uint*)(src + (max(0, looph - 1) * srcPitch) + (4 * min(width - 1, loopw + 1)));
102                 d = *cast(uint*)(src + (looph * srcPitch) + (4 * max(0, loopw - 1)));
103                 e = *cast(uint*)(src + (looph * srcPitch) + (4 * loopw));
104                 f = *cast(uint*)(src + (looph * srcPitch) + (4 * min(width - 1, loopw + 1)));
105                 g = *cast(uint*)(src + (min(height - 1, looph + 1) * srcPitch) + (4 * max(0, loopw - 1)));
106                 h = *cast(uint*)(src + (min(height - 1, looph + 1) * srcPitch) + (4 * loopw));
107                 i = *cast(uint*)(src + (min(height - 1, looph + 1) * srcPitch) + (4 * min(width - 1, loopw + 1)));
108 
109                 if (b != h && d != f) {
110                     e0 = d == b ? d : e;
111                     e1 = (d == b && e != c) || (b == f && e != a) ? b : e;
112                     e2 = b == f ? f : e;
113                     e3 = (d == b && e != g) || (d == h && e != a) ? d : e;
114                     e4 = e;
115                     e5 = (b == f && e != i) || (h == f && e != c) ? f : e;
116                     e6 = d == h ? d : e;
117                     e7 = (d == h && e != i) || (h == f && e != g) ? h : e;
118                     e8 = h == f ? f : e;
119                 } else {
120                     e0 = e;
121                     e1 = e;
122                     e2 = e;
123                     e3 = e;
124                     e4 = e;
125                     e5 = e;
126                     e6 = e;
127                     e7 = e;
128                     e8 = e;
129                 }
130 
131                 *cast(uint*)(dst + looph * 3 * dstPitch + loopw * 3 * 4) = e0;
132                 *cast(uint*)(dst + looph * 3 * dstPitch + (loopw * 3 + 1) * 4) = e1;
133                 *cast(uint*)(dst + looph * 3 * dstPitch + (loopw * 3 + 2) * 4) = e2;
134                 *cast(uint*)(dst + (looph * 3 + 1) * dstPitch + loopw * 3 * 4) = e3;
135                 *cast(uint*)(dst + (looph * 3 + 1) * dstPitch + (loopw * 3 + 1) * 4) = e4;
136                 *cast(uint*)(dst + (looph * 3 + 1) * dstPitch + (loopw * 3 + 2) * 4) = e5;
137                 *cast(uint*)(dst + (looph * 3 + 2) * dstPitch + loopw * 3 * 4) = e6;
138                 *cast(uint*)(dst + (looph * 3 + 2) * dstPitch + (loopw * 3 + 1) * 4) = e7;
139                 *cast(uint*)(dst + (looph * 3 + 2) * dstPitch + (loopw * 3 + 2) * 4) = e8;
140             }
141         }
142 
143         return dst;
144     }
145 
146     private:
147         ubyte[] buffer;
148 }
149 
150 
151 class Scale3xFaster : Scaler {
152     this() {
153         this.buffer = new ubyte[NATIVE_WIDTH * 3 * NATIVE_HEIGHT * 3 * 4];
154     }
155 
156     int factor() {
157         return 3;
158     }
159 
160     ubyte* scale(ubyte* srcData) {
161         uint* src = cast(uint*)srcData;
162         uint* dst = cast(uint*)this.buffer.ptr;
163 
164         int width = NATIVE_WIDTH;
165         int height = NATIVE_HEIGHT;
166         int srcPitch = width;
167         int dstPitch = width * 3;
168 
169         // First row
170         scale3x_32_def_whole(dst, dst + dstPitch, dst + (dstPitch * 2),
171             src, src, src + srcPitch, width);
172 
173         // Middle rows
174         foreach(i; 1 .. 239) {
175             dst += dstPitch * 3;
176 
177             scale3x_32_def_whole(dst, dst + dstPitch, dst + (dstPitch * 2),
178                 src, src + srcPitch, src + (srcPitch * 2), width);
179 
180             src += srcPitch;
181         }
182 
183         // Last row
184         dst += dstPitch * 3;
185 
186         scale3x_32_def_whole(dst, dst + dstPitch, dst + (dstPitch * 2),
187             src, src + srcPitch, src + srcPitch, width);
188 
189         return  cast(ubyte*)this.buffer.ptr;
190     }
191 
192     private:
193         ubyte[] buffer;
194 
195         void scale3x_32_def_whole(uint* dst0, uint* dst1, uint* dst2, const(uint)* src0,
196                                   const(uint)* src1, const(uint)* src2, uint count)
197         {
198             assert(count >= 2);
199 
200             /* first pixel */
201             if (src0[0] != src2[0] && src1[0] != src1[1]) {
202                 dst0[0] = src1[0];
203                 dst0[1] = (src1[0] == src0[0] && src1[0] != src0[1]) || (src1[1] == src0[0] && src1[0] != src0[0]) ? src0[0] : src1[0];
204                 dst0[2] = src1[1] == src0[0] ? src1[1] : src1[0];
205                 dst1[0] = (src1[0] == src0[0] && src1[0] != src2[0]) || (src1[0] == src2[0] && src1[0] != src0[0]) ? src1[0] : src1[0];
206                 dst1[1] = src1[0];
207                 dst1[2] = (src1[1] == src0[0] && src1[0] != src2[1]) || (src1[1] == src2[0] && src1[0] != src0[1]) ? src1[1] : src1[0];
208                 dst2[0] = src1[0];
209                 dst2[1] = (src1[0] == src2[0] && src1[0] != src2[1]) || (src1[1] == src2[0] && src1[0] != src2[0]) ? src2[0] : src1[0];
210                 dst2[2] = src1[1] == src2[0] ? src1[1] : src1[0];
211             } else {
212                 dst0[0] = src1[0];
213                 dst0[1] = src1[0];
214                 dst0[2] = src1[0];
215                 dst1[0] = src1[0];
216                 dst1[1] = src1[0];
217                 dst1[2] = src1[0];
218                 dst2[0] = src1[0];
219                 dst2[1] = src1[0];
220                 dst2[2] = src1[0];
221             }
222             ++src0;
223             ++src1;
224             ++src2;
225             dst0 += 3;
226             dst1 += 3;
227             dst2 += 3;
228 
229             /* central pixels */
230             count -= 2;
231             while (count) {
232                 if (src0[0] != src2[0] && src1[-1] != src1[1]) {
233                     dst0[0] = src1[-1] == src0[0] ? src1[-1] : src1[0];
234                     dst0[1] = (src1[-1] == src0[0] && src1[0] != src0[1]) || (src1[1] == src0[0] && src1[0] != src0[-1]) ? src0[0] : src1[0];
235                     dst0[2] = src1[1] == src0[0] ? src1[1] : src1[0];
236                     dst1[0] = (src1[-1] == src0[0] && src1[0] != src2[-1]) || (src1[-1] == src2[0] && src1[0] != src0[-1]) ? src1[-1] : src1[0];
237                     dst1[1] = src1[0];
238                     dst1[2] = (src1[1] == src0[0] && src1[0] != src2[1]) || (src1[1] == src2[0] && src1[0] != src0[1]) ? src1[1] : src1[0];
239                     dst2[0] = src1[-1] == src2[0] ? src1[-1] : src1[0];
240                     dst2[1] = (src1[-1] == src2[0] && src1[0] != src2[1]) || (src1[1] == src2[0] && src1[0] != src2[-1]) ? src2[0] : src1[0];
241                     dst2[2] = src1[1] == src2[0] ? src1[1] : src1[0];
242                 } else {
243                     dst0[0] = src1[0];
244                     dst0[1] = src1[0];
245                     dst0[2] = src1[0];
246                     dst1[0] = src1[0];
247                     dst1[1] = src1[0];
248                     dst1[2] = src1[0];
249                     dst2[0] = src1[0];
250                     dst2[1] = src1[0];
251                     dst2[2] = src1[0];
252                 }
253 
254                 ++src0;
255                 ++src1;
256                 ++src2;
257                 dst0 += 3;
258                 dst1 += 3;
259                 dst2 += 3;
260                 --count;
261             }
262 
263             /* last pixel */
264             if (src0[0] != src2[0] && src1[-1] != src1[0]) {
265                 dst0[0] = src1[-1] == src0[0] ? src1[-1] : src1[0];
266                 dst0[1] = (src1[-1] == src0[0] && src1[0] != src0[0]) || (src1[0] == src0[0] && src1[0] != src0[-1]) ? src0[0] : src1[0];
267                 dst0[2] = src1[0];
268                 dst1[0] = (src1[-1] == src0[0] && src1[0] != src2[-1]) || (src1[-1] == src2[0] && src1[0] != src0[-1]) ? src1[-1] : src1[0];
269                 dst1[1] = src1[0];
270                 dst1[2] = (src1[0] == src0[0] && src1[0] != src2[0]) || (src1[0] == src2[0] && src1[0] != src0[0]) ? src1[0] : src1[0];
271                 dst2[0] = src1[-1] == src2[0] ? src1[-1] : src1[0];
272                 dst2[1] = (src1[-1] == src2[0] && src1[0] != src2[0]) || (src1[0] == src2[0] && src1[0] != src2[-1]) ? src2[0] : src1[0];
273                 dst2[2] = src1[0];
274             } else {
275                 dst0[0] = src1[0];
276                 dst0[1] = src1[0];
277                 dst0[2] = src1[0];
278                 dst1[0] = src1[0];
279                 dst1[1] = src1[0];
280                 dst1[2] = src1[0];
281                 dst2[0] = src1[0];
282                 dst2[1] = src1[0];
283                 dst2[2] = src1[0];
284             }
285         }
286 }